package com.ihome.framework.core.dubbo.hystrix;

import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcContext;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;

public class DubboHystrixCommand extends HystrixCommand<Result> {

	private Invoker<?> invoker;
	private Invocation invocation;
	private RpcContext rpcContext;

	private static final String GENERIC_METHOD_PREFIX = "genericMethod-";

	public DubboHystrixCommand(Invoker<?> invoker, Invocation invocation, HystrixConfig hystrix,
			RpcContext rpcContext) {
		super(Setter
				.withGroupKey(HystrixCommandGroupKey.Factory.asKey(invoker.getUrl().getParameter("generic") == null
						? invoker.getInterface().getName() : invoker.getUrl().getParameter("interface")))
				.andCommandKey(HystrixCommandKey.Factory.asKey(String.format("%s_%d",
						invoker.getUrl().getParameter("generic") == null ? invocation.getMethodName()
								: GENERIC_METHOD_PREFIX + invocation.getArguments()[0],
						invocation.getArguments() == null ? 0 : invocation.getArguments().length)))
				.andCommandPropertiesDefaults(
						// 熔断器是否打开
						HystrixCommandProperties.Setter().withCircuitBreakerEnabled(hystrix.isEnabled())
								// 表示请求数至少10秒钟内达到多大才进行熔断计算，熔断器才发挥起作用
								.withCircuitBreakerRequestVolumeThreshold(hystrix.getRequestVolumeThreshold())
								// 熔断器中断请求一段时间后会进入半打开状态,放部分流量(一次请求)过去重试,如果失败熔断器继续打开，等待下一次再重试
								.withCircuitBreakerSleepWindowInMilliseconds(hystrix.getSleepWindowInMilliseconds())
								// 熔断器错误比率阈值
								.withCircuitBreakerErrorThresholdPercentage(hystrix.getErrorThresholdPercentage())
								// 执行超时开关
								.withExecutionTimeoutEnabled(hystrix.isTimeoutEnabled())
								// 执行超时时间
								.withExecutionTimeoutInMilliseconds(hystrix.getTimeoutInMilliseconds()))
				.andThreadPoolPropertiesDefaults(
						// 线程池核心线程数量
						HystrixThreadPoolProperties.Setter().withCoreSize(hystrix.getCoreSize())
								// 当等待队列多大的时候，将会执行决绝策略
								.withQueueSizeRejectionThreshold(hystrix.getQueueSizeRejectionThreashold())
								// 空闲线程保活时间
								.withKeepAliveTimeMinutes(hystrix.getKeepAliveTime())
								// 最大队列大小，如果-1则会使用交换队列
								.withMaxQueueSize(hystrix.getMaxQueueSize())));

		this.invoker = invoker;
		this.invocation = invocation;
		this.rpcContext = rpcContext;
	}

	@Override
	protected Result run() throws Exception {
		RpcContext.getContext().setInvoker(invoker).setInvocation(invocation)
				.setLocalAddress(NetUtils.getLocalHost(), 0)
				.setRemoteAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort());
		RpcContext.getContext().setArguments(rpcContext.getArguments());
		RpcContext.getContext().setAttachments(rpcContext.getAttachments());
		RpcContext.getContext().setFuture(rpcContext.getFuture());
		RpcContext.getContext().setUrl(rpcContext.getUrl());
		RpcContext.getContext().setUrls(rpcContext.getUrls());
		RpcContext.getContext().setMethodName(rpcContext.getMethodName());
		RpcContext.getContext().setParameterTypes(rpcContext.getParameterTypes());
		return invoker.invoke(invocation);
	}
}